package com.wanglianyun.config.datasource;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Primary;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

/**
 * Description: 配置数据源基本信息，并将所有数据源添加到动态数据源中进行进行管理
 * Copyright: Copyright (c) 2024
 * Create Time: 2024-11-13 16:21
 *
 * @author wanglianyun
 */
@Configuration
public class DynamicDataSourceConfig {

    @Value("${spring.datasource.type}")
    private Class<? extends DataSource> dataSourceType;

    @Value("${spring.datasource.druid-pool.initial-size}")
    private int initialSize;
    @Value("${spring.datasource.druid-pool.max-active}")
    private int maxActive;
    @Value("${spring.datasource.druid-pool.max-wait}")
    private int maxWait;
    @Value("${spring.datasource.druid-pool.min-idle}")
    private int minIdle;

    // @Primary在多个相同类型的bean中指定一个默认的bean，当Spring容器在自动装配时遇到类型冲突，即存在多个相同类型的bean时，
    // 如果没有使用@Qualifier或其他方式指定具体的bean，Spring会自动选择标记有@Primary的bean进行注入。(该注解在这可要可不要)
    @Primary
    @Bean("studentDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.db1")
    public DataSource masterDataSource() {
        DruidDataSource druidDataSource = (DruidDataSource) DataSourceBuilder.create().type(dataSourceType).build();
        druidPoolConfig(druidDataSource);
        return druidDataSource;
    }

    @Bean("teacherDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.db2")
    public DataSource slaveDataSource() {
        DruidDataSource druidDataSource = (DruidDataSource) DataSourceBuilder.create().type(dataSourceType).build();
        druidPoolConfig(druidDataSource);
        return druidDataSource;
    }

    private void druidPoolConfig(DruidDataSource druidDataSource) {
        druidDataSource.setMaxActive(maxActive);
        druidDataSource.setMaxWait(maxWait);
        druidDataSource.setInitialSize(initialSize);
        druidDataSource.setMinIdle(minIdle);
    }

    @Bean("dynamicDataSource")
    // @DependsOn注解可以定义在类和方法上，被依赖的组件会比该组件先注册到IOC容器中，
    // 即先创建studentDataSource，再创建teacherDataSource，最后创建dynamicDataSource
    @DependsOn({"studentDataSource", "teacherDataSource"})
    public DataSource dataSource(@Qualifier("studentDataSource") DataSource masterDataSource, @Qualifier("teacherDataSource") DataSource slaveDataSource) {
        Map<Object, Object> dataSourceMap = new HashMap<>();
        dataSourceMap.put("studentDataSource", masterDataSource);
        dataSourceMap.put("teacherDataSource", slaveDataSource);
        //AbstractRoutingDataSource的核心原理是通过一个线程本地变量（ThreadLocal）来保存当前使用的数据源标识，
        // 然后在每次获取连接时，根据这个标识来决定使用哪个数据源。
        DynamicDataSourceContextHolder.dataSourceNames.add("studentDataSource");
        DynamicDataSourceContextHolder.dataSourceNames.add("teacherDataSource");
        // 设置动态数据源
        return new DynamicDataSource(masterDataSource, dataSourceMap);
    }
}

